#!/usr/bin/env python
# -*- coding:utf-8 -*-
"""
浏览器对象开发
1、浏览器的类型(chrome,ie,firefox,edge,opera)
2、浏览器的启动参数(无头化，最大化，尺寸化)
3、浏览器的属性(显示尺寸，隐式等待/页面加载/js执行时间)
"""
from typing import Type, Union
from selenium.webdriver import *
from selenium.webdriver.opera.options import Options as OperaOptions
from selenium.webdriver.edge.options import Options as EdgeOptions
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from setting.setting import *

BROWSERS = (Chrome, Ie, Firefox, Edge, Opera, PhantomJS, Remote)
OPTIONS = (ChromeOptions, IeOptions, FirefoxOptions, EdgeOptions, OperaOptions)


class BrowserTypeError(Exception):
    """自定义异常：浏览器类型错误"""

    def __init__(self, _type):
        self._type = _type

    def __str__(self):
        return f'不支持该浏览器类型：{self._type}'


class BrowserBase(object):
    """浏览器基类"""
    windows_size = WINDOWS_SIZE  # 浏览器显示尺寸
    implicit_waiting = IMPLICIT_WAIT_TIME  # 隐式等待时间,单位秒
    page_load_time = PAGE_LOAD_TIME  # 页面加载时间，单位秒
    script_timeout = SCRIPT_TIMEOUT  # js执行时间,单位秒
    headless = HEADLESS  # 默认无头化启动

    # 浏览器启动路径
    chrome_driver_path = CHROME_DRIVER_PATH
    edge_driver_path = EDGE_DRIVER_PATH
    firefox_driver_path = FIREFOX_DRIVER_PATH
    opera_driver_path = OPERA_DRIVER_PATH
    # 使用32位ie驱动解决文本输入缓慢的问题
    ie_driver_path = IE_DRIVER_PATH

    def __init__(self, browser_type: Type[Union[Chrome, Ie, Firefox, Edge, Opera, PhantomJS, Remote]] = Chrome,
                 option_type: Type[
                     Union[FirefoxOptions, ChromeOptions, IeOptions, EdgeOptions, OperaOptions]] = ChromeOptions,
                 driver_path: str = chrome_driver_path,
                 command_executor: str = 'http://127.0.0.1:4444/wd/hub'):
        """
        默认使用谷歌浏览器
        :param browser_type: 浏览器类型
        :param option_type: 启动参数类型
        :param driver_path: 驱动路径
        :param command_executor: 远程启动服务URL
        """
        # 初始化浏览器对象，首先对浏览器类型、参数类型和驱动路径参数进行校验
        if not issubclass(browser_type, BROWSERS):
            raise BrowserTypeError(browser_type)
        if not issubclass(option_type, OPTIONS):
            raise BrowserTypeError(option_type)
        if not isinstance(driver_path, str):
            raise TypeError
        self._browser = browser_type
        self._option = option_type
        self._path = driver_path
        self._url = command_executor

    @property
    def options(self):
        """浏览器特定操作，在子类中实现"""
        return

    @property
    def browser(self):
        """
        启动浏览器，返回浏览器实例
        :return:
        """
        return


class CHROME(BrowserBase):
    """Chrome浏览器启动类"""
    windows_size = CHROME_WINDOW_SIZE  # 浏览器显示尺寸
    start_maximized = CHROME_START_MAXIMIZED  # 浏览器最大化
    method_mark = CHROME_METHOD_MARK  # 控制浏览器启动特性是否设置
    option_mark = CHROME_OPTION_MARK  # 控制浏览器参数是否添加

    # 实验选项
    experimental_option = CHROME_EXPERIMENTAL_OPTION

    @property
    def options(self):
        """chrome浏览器特有操作属性"""
        if self.option_mark:
            chrome_option = self._option()
            chrome_option.add_argument(self.start_maximized)
            for k, v in self.experimental_option.items():
                chrome_option.add_experimental_option(k, v)
            chrome_option.headless = self.headless
            return chrome_option
        return None

    @property
    def browser(self):
        # 本地启动启动还是远程启动，是否需要启动控制参数
        if self._browser == Remote:
            if self.options:
                chrome = self._browser(self._url, options=self.options, desired_capabilities=DesiredCapabilities.CHROME)
            else:
                chrome = self._browser(self._url, desired_capabilities=DesiredCapabilities.CHROME)
        else:
            if self.options:
                chrome = self._browser(self._path, options=self.options)
            else:
                chrome = self._browser(self._path)

        if self.method_mark:
            chrome.implicitly_wait(self.implicit_waiting)
            chrome.set_script_timeout(self.script_timeout)
            chrome.set_page_load_timeout(self.page_load_time)
        # chrome.set_window_size(*self.windows_size)
        return chrome


class IE(BrowserBase):
    clean_session = IE_CLEAN_SESSION  # 清空本地会话
    ignoreZoomSetting = IE_IGNORE_ZOOM_SETTING  # 忽略浏览器的缩放设置
    method_mark = IE_METHOD_MARK  # 控制浏览器启动特性是否设置
    option_mark = IE_OPTION_MARK  # 控制浏览器参数是否添加
    attach_timeout = IE_ATTACH_TIMEOUT  # 超时时间

    def __init__(self):
        super(IE, self).__init__(
            browser_type=Ie,
            option_type=IeOptions,
            driver_path=super().ie_driver_path
        )

    @property
    def options(self):
        if self.option_mark:
            ie_option = self._option()
            ie_option.ensure_clean_session = self.clean_session
            ie_option.ignore_zoom_level = self.ignoreZoomSetting
            ie_option.browser_attach_timeout = self.attach_timeout
            return ie_option
        return None

    @property
    def browser(self):
        if self.options:
            ie = self._browser(self._path, options=self.options)
        else:
            ie = self._browser(self._path)
        if self.method_mark:
            ie.implicitly_wait(self.implicit_waiting)
            # ie.set_window_size(*self.windows_size)
            ie.set_page_load_timeout(self.page_load_time)
            ie.set_script_timeout(self.script_timeout)
            ie.maximize_window()
        return ie


if __name__ == '__main__':
    import time

    driver = CHROME().browser
    driver.get('https://www.baidu.com')
    time.sleep(2)
    driver.quit()
